package com.lcm.weam.util;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.event.ProgressEvent;
import com.aliyun.oss.event.ProgressEventType;
import com.aliyun.oss.event.ProgressListener;
import com.aliyun.oss.model.*;
import com.lcm.weam.entity.sys.FileList;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 阿里云对象存储工具类
 */
public class AliOSSUtil implements ProgressListener {

    private final String prefix = "https://";
    private final String endpoint = "oss-cn-beijing.aliyuncs.com";
    private final String accessKeyId = "LTAI5tCR47BJYDEgq8JyC8Gx";
    private final String accessKeySecret = "RgqKvqk6m6GZS4i6GvLD9clVjFOPHG";
    private final String bucketName = "weam";

    //进度条
    private long bytesWritten = 0;
    private long totalBytes = -1;
    private boolean succeed = false;

    /**
     * 上传文件
     *  file:前端发送的文件；dir:文件路径；filename:带后缀文件名；bool: true为私有、false为公共读
     */
    public String upload(MultipartFile file, String dir, String filename, boolean bool) throws IOException {
        OSS ossClient = new OSSClientBuilder().build(prefix + endpoint, accessKeyId, accessKeySecret); //未加密客户端
        //传了名字就用，不传就用原文件名
        if (filename == null || "".equals(filename)){
            filename = file.getOriginalFilename(); //带后缀
        }else{
            filename += Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf('.'));
        }
        if (dir == null) {
            dir = "";
        }
        InputStream inputStream = file.getInputStream();
        String objectName = dir +'/'+ filename;
        // 创建PutObjectRequest对象。
        // 依次填写Bucket名称（例如examplebucket）、Object完整路径（例如exampledir/exampleobject.txt）和本地文件的完整路径。Object完整路径中不能包含Bucket名称。
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, inputStream);
        //配置元信息
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setHeader("x-oss-forbid-overwrite", "false"); //true为不允许覆盖同名文件，报错，false为允许
        putObjectRequest.setMetadata(metadata);
        //上传
        ossClient.putObject(putObjectRequest);
        // 设置文件的访问权限为公共读。
        if (!bool){
            ossClient.setObjectAcl(bucketName, objectName, CannedAccessControlList.PublicRead);
        }
        inputStream.close();
        ossClient.shutdown();
        if (dir.isBlank()){
            return prefix + bucketName + "." + endpoint + "/" + filename;
        }else{
            return prefix + bucketName + "." + endpoint + "/" + dir + "/" + filename;
        }
    }

    //加密上传文件
    public String uploadXXX(MultipartFile file, String dir, String filename){
        return null;
    }

    //分页列举指定前缀文件 page：当前页数  num：一页项目数
    public Map<String,Object> listFiles(String dir,int page,int num){
        OSS ossClient = new OSSClientBuilder().build(prefix + endpoint, accessKeyId, accessKeySecret); //未加密客户端
        String nextContinuationToken = null; //分页token
        ListObjectsV2Result result = null; //列举结果集
        Map<String, Object> map = new HashMap<>();
        List<FileList> list = new ArrayList<>(num + 1); //数据列表
        int i = 1; //页码序号
        do {
            ListObjectsV2Request request = new ListObjectsV2Request(bucketName);
            request.setMaxKeys(num);
            request.setPrefix(dir);
            request.setContinuationToken(nextContinuationToken);
            //查询
            result = ossClient.listObjectsV2(request);
            List<OSSObjectSummary> sums = result.getObjectSummaries();
            map.put("nums",result.getKeyCount());
            if (i == page){
                for (OSSObjectSummary sum : sums) {
                    FileList fileList = new FileList();
                    fileList.setName(sum.getKey().replace(dir + "/",""));
                    fileList.setSize(sum.getSize());
                    fileList.setUploadTime(sum.getLastModified());
                    list.add(fileList);
                }
            }
            nextContinuationToken = result.getNextContinuationToken();
            i++;
        }while (result.isTruncated());
        ossClient.shutdown();
        map.put("total",i-1);
        map.put("rows",list);
        return map;
    }

    /**
     * 下载文件(通过文件名获取输入流)
     */
    public OSS download(){
        return new OSSClientBuilder().build(prefix + endpoint, accessKeyId, accessKeySecret); //使用后必须关闭
    }

    /**
     *  删除文件
     *   fileName：带路径和后缀的文件名
     */
    public String detete(String fileName) {
        OSS ossClient = new OSSClientBuilder().build(prefix + endpoint, accessKeyId, accessKeySecret); //未加密客户端
        ossClient.deleteObject(bucketName, fileName);
        ossClient.shutdown();
        return "删除成功";
    }



    /**
     *  以下代码只在测试代码中使用过，系统中未使用
     */
    //获取授权url
    public URL getUrl(String path) {
        OSS ossClient = new OSSClientBuilder().build(prefix + endpoint, accessKeyId, accessKeySecret); //未加密客户端
        Date expiration = new Date(new Date().getTime() + 30 * 1000);
        URL url = ossClient.generatePresignedUrl(bucketName, path, expiration);
        ossClient.shutdown();
        return url;
    }

    public String getPath(String originURL) {
        return originURL.replace("http://" + bucketName + "." + endpoint + "/", "");
    }


    public ByteArrayOutputStream getOutputStream(String originURL) throws IOException {
        OSS ossClient = new OSSClientBuilder().build(prefix + endpoint, accessKeyId, accessKeySecret); //未加密客户端
        String path = getPath(originURL);
        OSSObject ossObject = ossClient.getObject(bucketName, path);
        InputStream inputStream = ossObject.getObjectContent();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        int len;
        byte[] bytes = new byte[1024];
        while ((len = inputStream.read(bytes)) != -1) {
            outputStream.write(bytes, 0, len);
        }
        ossClient.shutdown();
        return outputStream;
    }

    public static void download(ByteArrayOutputStream byteArrayOutputStream, HttpServletResponse response, String returnName) throws IOException {
        response.setContentType("application/octet-stream;charset=UTF-8");
        returnName = response.encodeURL(new String(returnName.getBytes(), StandardCharsets.UTF_8));            //保存的文件名,必须和页面编码一致,否则乱码
        response.addHeader("Content-Disposition", "attachment;filename=" + returnName);
        response.setContentLength(byteArrayOutputStream.size());
        response.addHeader("Content-Length", "" + byteArrayOutputStream.size());
        ServletOutputStream outputstream = response.getOutputStream();    //取得输出流
        byteArrayOutputStream.writeTo(outputstream);                    //写到输出流
        byteArrayOutputStream.close();                                    //关闭
        outputstream.flush();                                            //刷数据
    }

    private File multipartFileToFile(MultipartFile file) throws IOException {
        File toFile = null;
        if ("".equals(file) || file.getSize() <= 0) {
            file = null;
        } else {
            InputStream ins = null;
            ins = file.getInputStream();
            toFile = new File(Objects.requireNonNull(file.getOriginalFilename()));
            OutputStream os = new FileOutputStream(toFile);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        }
        return toFile;
    }

    private void delteTempFile(File file) {
        if (file != null) {
            File del = new File(file.toURI());
            del.delete();
        }
    }

    @Override
    public void progressChanged(ProgressEvent progressEvent) {
        long bytes = progressEvent.getBytes();
        ProgressEventType eventType = progressEvent.getEventType();
        switch (eventType) {
            case TRANSFER_STARTED_EVENT -> System.out.println("Start to upload......");
            case REQUEST_CONTENT_LENGTH_EVENT -> {
                this.totalBytes = bytes;
                System.out.println(this.totalBytes + " bytes in total will be uploaded to OSS");
            }
            case REQUEST_BYTE_TRANSFER_EVENT -> {
                this.bytesWritten += bytes;
                if (this.totalBytes != -1) {
                    int percent = (int) (this.bytesWritten * 100.0 / this.totalBytes);
                    System.out.println(bytes + " bytes have been written at this time, upload progress: " + percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");
                } else {
                    System.out.println(bytes + " bytes have been written at this time, upload ratio: unknown" + "(" + this.bytesWritten + "/...)");
                }
            }
            case TRANSFER_COMPLETED_EVENT -> {
                this.succeed = true;
                System.out.println("Succeed to upload, " + this.bytesWritten + " bytes have been transferred in total");
            }
            case TRANSFER_FAILED_EVENT -> System.out.println("Failed to upload, " + this.bytesWritten + " bytes have been transferred");
            default -> {
            }
        }
    }

    public boolean isSucceed() {
        return succeed;
    }
}
